package br.com.dev.tcc.view.mapa;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Intent;
import android.location.Location;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
import br.com.dev.tcc.R;
import br.com.dev.tcc.adapter.ListaLocaisAdapter;
import br.com.dev.tcc.bean.MapLocal;
import br.com.dev.tcc.constants.ExtrasConstants;
import br.com.dev.tcc.singleton.LocationSingleton;

import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.SubMenu;
import com.actionbarsherlock.view.Window;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;

public class MapaLocais extends SherlockFragmentActivity {

	private SupportMapFragment mapFragment;
	private GoogleMap map;
	private ArrayList<MapLocal> locales;
	private MapNearLocalesAsync searchLocalesAsyncTask;
	private MapLocationDetailAsync searchDetailsAsyncTask;

	private View listContainer;
	private View mapContainer;
	private Animation showAnim;
	private Animation hideAnim;
	private boolean isShowingList;
	private MenuItem menuItemLista;
	private MenuItem menuItemMapa;
	private ListView listaLocais;
	private boolean searchComplete;

	@Override
	protected void onCreate(Bundle params) {

		super.onCreate(null);

		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
		setSupportProgressBarIndeterminateVisibility(true);

		setContentView(R.layout.mapa);

		showAnim = getShowAnim();
		hideAnim = getHideAnim();

		listContainer = findViewById(R.id.containerList);

		listaLocais = (ListView) findViewById(R.id.list);

		getSupportActionBar().setTitle(
				getIntent().getStringExtra(ExtrasConstants.TITLE_TO_MAP));
		getSupportActionBar().setIcon(R.drawable.locais);
		getSupportActionBar().setHomeButtonEnabled(true);
		getSupportActionBar().setDisplayHomeAsUpEnabled(true);
		getSupportActionBar().setDisplayShowHomeEnabled(true);

		mapFragment = new SupportMapFragment() {
			@Override
			public void onActivityCreated(Bundle savedInstanceState) {

				map = getMap();
				super.onActivityCreated(savedInstanceState);
			}
		};

		mapContainer = findViewById(R.id.mapContainer);
		getSupportFragmentManager().beginTransaction()
				.replace(R.id.mapContainer, mapFragment).commit();
	}

	@Override
	protected void onStart() {

		super.onStart();
		map = mapFragment.getMap();

		if (map == null) {

			Toast.makeText(this, R.string.mapa_erro_conexao, Toast.LENGTH_LONG)
					.show();
		} else {

			map.setMapType(GoogleMap.MAP_TYPE_NORMAL);

			Location loc = LocationSingleton.getInstance().getLocation();
			LatLng latLng = null;

			if (loc != null)
				latLng = new LatLng(loc.getLatitude(), loc.getLongitude());

			map.setLocationSource(new LocationSource() {

				@Override
				public void deactivate() {
				}

				@Override
				public void activate(OnLocationChangedListener listener) {

					listener.onLocationChanged(LocationSingleton.getInstance()
							.getLocation());
				}
			});
			map.setMyLocationEnabled(true);

			CameraUpdate update = CameraUpdateFactory.newLatLngZoom(latLng, 15);
			map.animateCamera(update, 3000, null);

			map.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {

				@Override
				public void onInfoWindowClick(Marker marker) {

					for (MapLocal local : locales) {

						if (local.getName().equals(marker.getTitle())) {

							if (local.getPhoneNumber() != null) {

								Uri u = Uri.parse("tel:"
										+ local.getPhoneNumber());
								Intent it = new Intent(Intent.ACTION_DIAL);
								it.setData(u);
								it.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
								startActivity(it);
								return;
							}
						}
					}
				}
			});

			searchLocalesAsyncTask = new MapNearLocalesAsync();
			searchLocalesAsyncTask.execute(latLng);
		}
	}

	class MapNearLocalesAsync extends AsyncTask<LatLng, Void, String> {

		@Override
		protected String doInBackground(LatLng... loc) {

			String returnServices = "";
			try {

				if (!this.isCancelled())

					returnServices = getNearLocales(getIntent().getExtras()
							.getString(ExtrasConstants.SERVICE_TO_MAP), loc[0]);

			} catch (Exception e) {
				e.printStackTrace();
			}

			return returnServices;
		}

		@Override
		protected void onPostExecute(String result) {

			super.onPostExecute(result);

			locales = new ArrayList<MapLocal>();

			if (!this.isCancelled()) {

				JSONObject jsonObj = null;

				try {
					jsonObj = new JSONObject(result);

					JSONArray array = jsonObj.getJSONArray("results");

					if (array.length() > 0) {

						for (int i = 0; i < array.length(); i++) {

							JSONObject a = array.getJSONObject(i);
							String address = a.getString("formatted_address");
							String nome = a.getString("name");
							String reference = a.getString("reference");

							JSONObject gem = a.getJSONObject("geometry");
							JSONObject loc = gem.getJSONObject("location");
							double latitude = loc.getDouble("lat");
							double longitude = loc.getDouble("lng");

							LatLng l = new LatLng(latitude, longitude);

							addMarkerOnMap(l, nome, address);

							locales.add(new MapLocal().setAddress(address)
									.setLatLng(l).setName(nome)
									.setReference(reference));
						}
					} else {

						Toast.makeText(MapaLocais.this,
								R.string.locais_servico_nao_encontrado,
								Toast.LENGTH_LONG).show();
					}

				} catch (Exception e) {
					e.printStackTrace();
				}

				searchComplete = true;
				setListLocales();

				searchDetailsAsyncTask = new MapLocationDetailAsync();
				searchDetailsAsyncTask.execute();
			}
		}
	}

	class MapLocationDetailAsync extends AsyncTask<Void, Void, Boolean> {

		@Override
		protected Boolean doInBackground(Void... params) {

			if (!this.isCancelled()) {

				if (locales.size() >= 3) {
					for (int i = 0; i < 2; i++) {

						if (!this.isCancelled()) {

							MapLocal local = locales.get(i);
							String reference = local.getReference();

							try {
								String result = getLocaleDetails(reference);

								JSONObject jsonObj = new JSONObject(result);
								JSONObject jsonResults = jsonObj
										.getJSONObject("result");

								String phone = jsonResults
										.getString("formatted_phone_number");

								local.setPhoneNumber(phone);
							} catch (Exception e) {

								e.printStackTrace();
							}

							return true;
						}
					}
				} else
					return true;
			}

			return false;
		}

		@Override
		protected void onPostExecute(Boolean result) {

			setSupportProgressBarIndeterminateVisibility(false);
			if (!result) {
				Toast.makeText(MapaLocais.this, R.string.mapa_erro_conexao,
						Toast.LENGTH_LONG).show();
				finish();
			}
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		SubMenu subMenu1 = menu.addSubMenu(null);
		menuItemMapa = subMenu1.getItem();
		menuItemMapa.setIcon(android.R.drawable.ic_menu_mapmode);
		menuItemMapa.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
		menuItemMapa.setChecked(true);

		SubMenu subMenu2 = menu.addSubMenu(null);
		menuItemLista = subMenu2.getItem();
		menuItemLista.setIcon(android.R.drawable.ic_menu_view);
		menuItemLista.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
		menuItemLista.setChecked(true);

		menuItemMapa.setVisible(false);
		menuItemLista.setVisible(true);

		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		if (item.isChecked()) {

			if (isShowingList) {

				hideList();
			} else {

				if (searchComplete) {
					showList();
				}
			}
		} else {

			cancelAsyncs();
			finish();
		}

		return true;
	}

	@Override
	public void onBackPressed() {

		if (isShowingList) {

			hideList();
		} else {

			cancelAsyncs();

			super.onBackPressed();
		}
	}

	private void cancelAsyncs() {

		if (searchLocalesAsyncTask != null)
			searchLocalesAsyncTask.cancel(true);
		if (searchDetailsAsyncTask != null)
			searchDetailsAsyncTask.cancel(true);
	}

	// Busca JSON com locais próximos...
	private String getNearLocales(String service, LatLng latLng)
			throws Exception {

		HttpClient httpclient = new DefaultHttpClient();

		String uri = "https://maps.googleapis.com/maps/api/place/textsearch/json?query="
				+ service
				+ "+in+"
				+ latLng.latitude
				+ ","
				+ latLng.longitude
				+ "&sensor=true&key=AIzaSyDcV96ZP_muL8ZsSUYMlANDaGnxTZ8isLQ";

		HttpResponse response = httpclient.execute(new HttpGet(uri));
		StatusLine statusLine = response.getStatusLine();
		if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			response.getEntity().writeTo(out);
			out.close();
			String responseString = out.toString();
			return responseString;
		} else {

			response.getEntity().getContent().close();
			throw new IOException(statusLine.getReasonPhrase());
		}
	}

	// Busca JSON com detalhes do local
	private String getLocaleDetails(String reference) throws Exception {

		HttpClient httpclient = new DefaultHttpClient();

		String uri = "https://maps.googleapis.com/maps/api/place/details/json?reference="
				+ reference
				+ "&sensor=true&key=AIzaSyDcV96ZP_muL8ZsSUYMlANDaGnxTZ8isLQ";

		HttpResponse response = httpclient.execute(new HttpGet(uri));
		StatusLine statusLine = response.getStatusLine();
		if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			response.getEntity().writeTo(out);
			out.close();
			String responseString = out.toString();
			return responseString;
		} else {

			response.getEntity().getContent().close();
			throw new IOException(statusLine.getReasonPhrase());
		}
	}

	private void setListLocales() {

		listaLocais.setAdapter(new ListaLocaisAdapter(this, locales));

		listaLocais.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> a, View view, int position,
					long id) {

				requestRoute(locales.get(position).getLatLng());
			}
		});
	}

	private Animation getShowAnim() {

		return AnimationUtils.loadAnimation(this, R.anim.list_in);
	}

	private Animation getHideAnim() {

		return AnimationUtils.loadAnimation(this, R.anim.list_out);
	}

	private void addMarkerOnMap(LatLng l, String nome, String address) {

		if (map != null) {
			MarkerOptions m = new MarkerOptions();
			m.position(l).title(nome).snippet(address);
			map.addMarker(m);
		}
	}

	private void addAllMarkers() {

		for (MapLocal local : locales) {

			MarkerOptions m = new MarkerOptions();
			m.position(local.getLatLng()).title(local.getName())
					.snippet(local.getAddress());
			map.addMarker(m);
		}
	}

	private void showList() {

		menuItemMapa.setVisible(true);
		menuItemLista.setVisible(false);

		listContainer.setVisibility(View.VISIBLE);
		listContainer.startAnimation(showAnim);
		isShowingList = true;

		listContainer.bringToFront();
	}

	private void hideList() {

		menuItemMapa.setVisible(false);
		menuItemLista.setVisible(true);

		listContainer.startAnimation(hideAnim);
		listContainer.setVisibility(View.GONE);
		isShowingList = false;

		mapContainer.bringToFront();
	}

	private void requestRoute(LatLng destination) {

		Location location = LocationSingleton.getInstance().getLocation();

		LatLng origin = new LatLng(location.getLatitude(),
				location.getLongitude());

		new RequestDirections().execute(origin, destination);
	}

	private class RequestDirections extends
			AsyncTask<LatLng, Void, PolylineOptions> {

		@Override
		protected PolylineOptions doInBackground(LatLng... params) {

			LatLng origin = params[0];
			LatLng destination = params[1];

			try {
				String uri = "http://maps.googleapis.com/maps/api/directions/json?origin="
						+ origin.latitude
						+ ","
						+ origin.longitude
						+ "&destination="
						+ destination.latitude
						+ ","
						+ destination.longitude
						+ "&sensor=false&units=metric&mode=driving";

				HttpClient httpclient = new DefaultHttpClient();
				HttpResponse response = httpclient.execute(new HttpGet(uri));
				StatusLine statusLine = response.getStatusLine();

				if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
					ByteArrayOutputStream out = new ByteArrayOutputStream();
					response.getEntity().writeTo(out);
					out.close();

					String responseString = out.toString();

					List<LatLng> points = getRoute(responseString);

					return getPolylineOptions(points);
				} else {

					response.getEntity().getContent().close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

			return null;
		}

		private List<LatLng> getRoute(String route) {

			List<LatLng> points = new ArrayList<LatLng>();

			try {
				JSONObject jsonObj = new JSONObject(route);
				JSONArray array = jsonObj.getJSONArray("routes");
				jsonObj = array.getJSONObject(0);
				array = jsonObj.getJSONArray("legs");
				jsonObj = array.getJSONObject(0);
				array = jsonObj.getJSONArray("steps");

				if (array.length() > 0) {

					for (int i = 0; i < array.length(); i++) {

						JSONObject location = array.getJSONObject(i)
								.getJSONObject("start_location");
						double latitude = location.getDouble("lat");
						double longitude = location.getDouble("lng");

						LatLng l = new LatLng(latitude, longitude);

						points.add(l);

						location = array.getJSONObject(i).getJSONObject(
								"end_location");
						latitude = location.getDouble("lat");
						longitude = location.getDouble("lng");

						l = new LatLng(latitude, longitude);

						points.add(l);
					}
				}
			} catch (JSONException e) {
				e.printStackTrace();
			}

			return points;
		}

		private PolylineOptions getPolylineOptions(List<LatLng> points) {

			PolylineOptions lines = new PolylineOptions();
			lines.addAll(points);

			return lines;
		}

		@Override
		protected void onPostExecute(PolylineOptions lines) {

			hideList();
			lines = lines.width(lines.getWidth() / 2);
			lines = lines.color(getResources().getColor(
					R.color.abs__holo_blue_light));
			lines = lines.geodesic(true);
			map.clear();
			Location loc = LocationSingleton.getInstance().getLocation();
			CameraUpdate update = CameraUpdateFactory.newLatLngZoom(new LatLng(
					loc.getLatitude(), loc.getLongitude()), 15);
			map.animateCamera(update, 1500, null);
			addAllMarkers();
			map.addPolyline(lines);
		}
	}
}
